package org.yaac.shared.property;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;

import java.util.Arrays;
import java.util.List;

import org.yaac.shared.egql.ResultCell;
import org.yaac.shared.egql.ResultCell.ResultCellFactory;
import org.yaac.shared.util.StringUtils;

import com.google.common.base.Joiner;



/**
 * key can be a property type as well
 * 
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
//TODO : implement ordering based on logics documented at http://code.google.com/appengine/docs/java/datastore/entities.html
public class KeyInfo extends PropertyInfo {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * parent key, nullable
	 */
	private KeyInfo parent;
	
	/**
	 * key kind
	 */
	private String kind;
	
	/**
	 * key name, one of name and id is null
	 */
	private String name;

	/**
	 * id, one of name and id is null
	 */
	private Long id;
	
	/**
	 * encoded key string, this is mandatory
	 * 
	 * it will be used for client side to contruct file download path
	 */
	private String keyString;
	
	/**
	 * used by GWT
	 */
	@SuppressWarnings("unused")
	private KeyInfo() {}
	
	public KeyInfo(KeyInfo parent, String kind, String name, Long id, String keyString) {
		this.parent = parent;
		this.kind = kind;
		this.name = name;
		this.id = id;
		this.keyString = keyString;
	}
	
	public KeyInfo getParent() {
		return parent;
	}

	public String getName() {
		return name;
	}

	public Long getId() {
		return id;
	}
	
	public String getKind() {
		return kind;
	}

	public String getKeyString() {
		return keyString;
	}

	@Override
	public int compareTo(PropertyInfo o) {
		// TODO implement same logic documented at 
		// TODO http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/datastore/Key.html#compareTo(com.google.appengine.api.datastore.Key)
		return 0;
	}

	@Override
	public String asHtml() {
		StringBuilder sb = new StringBuilder();
		
		if (this.parent != null) {
			sb.append(this.parent.asHtml());
			sb.append("/<br/>");
		}

		sb.append("<a href='#editor:");
		sb.append(keyString);
		sb.append("'>");
		sb.append(this.asSingleNodeDesc());
		sb.append("</a>");
		
		return sb.toString();
	}
	
	public String asSingleNodeDesc() {
		String desc = isNullOrEmpty(name) ? id.toString() : "\"" + name + "\"";
		
		return this.kind + "(" + desc + ")";
	}
	
	public String asFullPathDesc() {
		if (this.parent == null) {
			return asSingleNodeDesc();
		} else {
			return this.parent.asFullPathDesc() + "/" + asSingleNodeDesc(); 
		}
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((keyString == null) ? 0 : keyString.hashCode());
		result = prime * result + ((kind == null) ? 0 : kind.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((parent == null) ? 0 : parent.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		KeyInfo other = (KeyInfo) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (keyString == null) {
			if (other.keyString != null)
				return false;
		} else if (!keyString.equals(other.keyString))
			return false;
		if (kind == null) {
			if (other.kind != null)
				return false;
		} else if (!kind.equals(other.kind))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (parent == null) {
			if (other.parent != null)
				return false;
		} else if (!parent.equals(other.parent))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "KeyInfo [parent=" + parent + ", kind=" + kind + ", name=" + name + ", id=" + id + ", keyString="
				+ keyString + "]";
	}

	@Override
	public ResultCell populateResultCell(ResultCellFactory factory) {
		ResultCell cell = super.populateResultCell(factory);
		cell.setType(PropertyType.KEY);
		cell.setParent(this.parent == null ? null : this.parent.populateResultCell(factory));
		cell.setChildren(null);
		cell.setPayload(Arrays.asList(kind, name, id.toString(), keyString));
		return cell;
	}

	@Override
	public String asExpression(FilePlaceHolder placeHolder) {
		List<String> terms = newLinkedList();
		
		KeyInfo curr = this;
		while(curr != null) {
			terms.add(0, isNullOrEmpty(curr.name) ? curr.id.toString() : StringUtils.wrap(curr.name, "'"));
			terms.add(0, StringUtils.wrap(curr.kind, "'"));
			
			curr = curr.parent;
		}
		
		return "key(" + Joiner.on(",").join(terms) + ")";
	}
	
	@Override
	public List<String> downloadPaths() {
		return newArrayList();
	}
}